﻿#include <iostream>
#include <vector>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <functional> //std::function  std::bind
#include <chrono>
using namespace std;
/*
线程池：
线程池是一种多线程处理形式，处理过程中将任务添加到队列，然后在后台线程中执行任务
每个线程都处理多个任务，因此减少了创建和销毁线程的额外开销
说简单点，就是开了几个线程，然后这几个线程循环去执行任务函数
减少了反复开辟线程而调用系统内核的资源开销

一个程序一个线程池就行了，可以采用单例模式
*/

class ThreadPool {
private:
	vector<thread> workers;       //线程池,存放线程(员工)
	queue<function<void()>> tasks; //任务队列,存放任务函数(工作安排)
	mutex mtx;                 //互斥锁，用于线程同步
	condition_variable cv;      //条件变量，用于线程同步
	bool stop;                  //线程池是否结束

public:
	ThreadPool(int numthread) : stop(false) {
		for (int i = 0; i < numthread; ++i) {
			//emplace_back()直接在容器里创建对象
			//不是对象完成构造后拷贝到容器内存中，相比push_back()效率更高
			workers.emplace_back([this] {
				while (true) {
					unique_lock<mutex> lock(this->mtx);
					//让员工等待工作
					cv.wait(lock, [this] {return !tasks.empty() || stop; });
					//到下班点了而且今天工作完了，下班(线程结束)
					if (stop && tasks.empty()) return;
					//使用move将任务函数变成右值进行赋值而不是左值触发拷贝，提高效率
					std::function<void()> task = std::move(tasks.front());
					tasks.pop();//任务队列里删除任务
					lock.unlock();//解锁
					task();  //执行任务
					std::this_thread::sleep_for(std::chrono::milliseconds(400));
				}
				});
		}
	}

	~ThreadPool() {//析构函数，线程池结束
		{
			unique_lock<mutex> lock(mtx);
			stop = true;
		}
		cv.notify_all(); //通知所有等待的线程，如果今天任务结束就下班了
		for (auto& t : workers) t.join(); //回收线程资源
	}

	template<class F, class ...Args> //可变参数模版
	void enqueue(F&& f, Args &&...args) {//函数模板中的&&，不仅是右值引用，还是左值引用
		//bind绑定，将函数和参数绑定在一起，返回一个可调用对象
		//函数返回值原函数决定，变成无参函数
		//使用function<void()> ，将函数转成无返回值函数(原有函数返回值被忽略)
		//forward完美转发​，保留原函数或者函数参数的右值属性，避免不必要的拷贝(不然所有都是以左值拷贝的)
		function<void()> task = std::bind(std::forward<F>(f), std::forward<Args>(args)...);
		unique_lock<mutex> lock(mtx);
		tasks.push(move(task));//使用move避免拷贝后，emplace和push本身的性能消耗是一样的
		lock.unlock();//向任务队列加任务后解锁
		cv.notify_one();  //通知一个等待的线程，有任务了
	}
};

int testThreadPool(int i) {
	cout << "task: " << i << endl;
	return i;
}

int main8() {
	ThreadPool pool(4);
	for (int i = 0; i < 10; ++i) {
		pool.enqueue(testThreadPool, i);
		//lamda表达式创建时候是右值
		//pool.enqueue([i] {cout << "task: " << i << endl; });
	}
	return 0;
}